#include "device_manager.hpp"
#include "p2pms_log.hpp"
#include "p2pms_util.hpp"

#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_device/include/audio_device_factory.h"
#include "modules/video_capture/video_capture_factory.h"
#include "modules/video_capture/video_capture.h"
#include "api/task_queue/default_task_queue_factory.h"

namespace p2pms
{

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
DeviceManager& DeviceManager::Instance()
{
	static DeviceManager instance;
	return instance;
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
DeviceManager::~DeviceManager()
{
	if (m_thread && m_adm) {
		m_thread->BlockingCall([=]() {
			m_adm->Terminate();
		});
	}
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
rtc::scoped_refptr<webrtc::AudioDeviceModule> DeviceManager::GetADM()
{
	assert(m_adm);

	return m_adm;
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
bool DeviceManager::StartPlayout()
{
	assert(m_adm);
	assert(m_thread);

	return m_thread->BlockingCall([=]() -> bool {
		if (!m_adm->PlayoutIsInitialized()) {
			if (m_adm->InitPlayout() != 0) {
				LOG_ERR("Failed to init playout");
				return false;
			}
			LOG_INF("Playout initialized successfully");
		}

		if (!m_adm->Playing()) {
			if (m_adm->StartPlayout() != 0) {
				LOG_ERR("Failed to start playout");
				return false;
			}
			LOG_INF("Playout started successfully");
		}
		
		return true;
	});
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
bool DeviceManager::StopPlayout()
{
	assert(m_adm);
	assert(m_thread);

	return m_thread->BlockingCall([=]() -> bool {
		if (m_adm->Playing()) {
			if (m_adm->StopPlayout() != 0) {
				LOG_ERR("Failed to stop playout");
				return false;
			}
			LOG_INF("Playout stopped successfully");
		}
		return true;
	});
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
bool DeviceManager::StartRecording()
{
	assert(m_adm);
	assert(m_thread);

	return m_thread->BlockingCall([=]() -> bool {
		if (!m_adm->RecordingIsInitialized()) {
			if (m_adm->InitRecording() != 0) {
				LOG_ERR("Failed to start recording");
				return false;
			}
			LOG_INF("Recording initialized successfully");
		}

		if (!m_adm->Recording()) {
			if (m_adm->StartRecording() != 0) {
				LOG_ERR("Failed to start recording");
				return false;
			}
			LOG_INF("Recording started successfully");
		}

		return true;
	});
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
bool DeviceManager::StopRecording()
{
	assert(m_adm);
	assert(m_thread);

	return m_thread->BlockingCall([=]() -> bool {
		if (m_adm->Recording()) {
			if (m_adm->StopRecording() != 0) {
				LOG_ERR("Failed to stop recording");
				return false;
			}
			LOG_INF("Recording stopped successfully");
		}
		return true;
	});
}

//--------------------------------------------------------------------------------------------------
// Called by P2PMSClient, which is in the sdk thread already.
//--------------------------------------------------------------------------------------------------
bool DeviceManager::Init(rtc::Thread* thread)
{
	if (!thread) {
		LOG_ERR("Invalid thread pointer");
		return false;
	}
	m_thread = thread;

	return m_thread->BlockingCall([=]() -> bool {
		m_adm = webrtc::AudioDeviceModule::Create(webrtc::AudioDeviceModule::kPlatformDefaultAudio,
			webrtc::CreateDefaultTaskQueueFactory().get());
		if (!m_adm) {
			LOG_ERR("Create audio device module failed");
			return false;
		}

		int32_t result = m_adm->Init();
		if (result != 0) {
			LOG_ERR("Init audio device module failed");
			return false;
		}

		LOG_INF("Init audio device module succeeded");
		
		return true;
	});
}

//--------------------------------------------------------------------------------------------------
// 获取媒体设备列表
//--------------------------------------------------------------------------------------------------
ErrCode DeviceManager::GetMediaDevList(MediaDeviceType dev_type, std::vector<MediaDevice>& devices)
{
	assert(m_adm);
	assert(m_thread);

	return SyncProxy<ErrCode>(m_thread, [&] {
		switch (dev_type) {
		case MediaDeviceType::AudioInput:
			return GetAudioInputDevices(devices);
		case MediaDeviceType::AudioOutput:
			return GetAudioOutputDevices(devices);
			break;
		case MediaDeviceType::VideoInput:
			return GetVideoInputDevices(devices);
			break;
		default:
			LOG_ERR("Unsupported device type: {}", static_cast<int>(dev_type));
			return ERR_INVALID_PARAM;
		}
	});
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
ErrCode DeviceManager::SelectDevice(const MediaDevice& device)
{
	assert(m_adm);
	assert(m_thread);

	return SyncProxy<ErrCode>(m_thread, [&] {
		if (!m_adm) {
			LOG_ERR("Invalid audio device module");
			return ERR_FAILED;
		}

		if (device.type == MediaDeviceType::AudioInput) {
			int result = m_adm->SetRecordingDevice(device.index);
			if (result != 0) {
				LOG_ERR("Set recording device failed: {}", result);
				return ERR_FAILED;
			}
			m_selected_devices.insert(std::make_pair(device.type, device));
			LOG_INF("Selected audio input device: index={}, name='{}', id='{}'",
				device.index, device.name, device.id);
		}
		else if (device.type == MediaDeviceType::AudioOutput) {
			int result = m_adm->SetPlayoutDevice(device.index);
			if (result != 0) {
				LOG_ERR("Set playout device failed: {}", result);
				return ERR_FAILED;
			}
			m_selected_devices.insert(std::make_pair(device.type, device));
			LOG_INF("Selected audio output device: index={}, name='{}', id='{}'",
				device.index, device.name, device.id);
		}
		else {
			LOG_ERR("Unsupported device type: {}", static_cast<int>(device.type));
			return ERR_FAILED;
		}

		return ERR_OK;
	});
}

//--------------------------------------------------------------------------------------------------
// 
//--------------------------------------------------------------------------------------------------
ErrCode DeviceManager::GetSelectedDevice(MediaDeviceType dev_type, MediaDevice& device)
{
	assert(m_adm);
	assert(m_thread);

	return SyncProxy<ErrCode>(m_thread, [&] {
		auto iter = m_selected_devices.find(dev_type);
		if (iter == m_selected_devices.end()) {
			LOG_WRN("Cannot find selected device for type:{}", dev_type);
			return ERR_FAILED;
		}
		device = iter->second;
		return ERR_OK;
	});
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------------------------
// 获取音频输入设备列表 (麦克风)
//--------------------------------------------------------------------------------------------------
ErrCode DeviceManager::GetAudioInputDevices(std::vector<MediaDevice>& devices)
{
	int16_t num_devices = m_adm->RecordingDevices();
	LOG_INF("Found {} audio input devices", num_devices);

	char device_name[webrtc::kAdmMaxDeviceNameSize] = { 0 };
	char device_guid[webrtc::kAdmMaxGuidSize] = { 0 };

	for (int i = 0; i < num_devices; i++) {
		if (m_adm->RecordingDeviceName(i, device_name, device_guid) == 0) {
			MediaDevice device;
			device.index = i;
			device.name = device_name;
			device.id = device_guid;
			device.type = MediaDeviceType::AudioInput;
			devices.push_back(device);
			LOG_INF("Audio input device {}: name='{}', id='{}'", i, device_name, device_guid);
		}
	}

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
// 获取音频输出设备列表 (扬声器)
//--------------------------------------------------------------------------------------------------
ErrCode DeviceManager::GetAudioOutputDevices(std::vector<MediaDevice>& devices)
{
	int16_t num_devices = m_adm->PlayoutDevices();
	LOG_INF("Found {} audio output devices", num_devices);

	char device_name[webrtc::kAdmMaxDeviceNameSize] = { 0 };
	char device_guid[webrtc::kAdmMaxGuidSize] = { 0 };

	for (int i = 0; i < num_devices; i++) {
		if (m_adm->PlayoutDeviceName(i, device_name, device_guid) == 0) {
			MediaDevice device;
			device.index = i;
			device.name = device_name;
			device.id = device_guid;
			device.type = MediaDeviceType::AudioOutput;
			devices.push_back(device);
			LOG_INF("Audio output device {}: name='{}', id='{}'", i, device_name, device_guid);
		}
	}

	return ERR_OK;
}

//--------------------------------------------------------------------------------------------------
// 获取视频输入设备列表 (摄像头)
//--------------------------------------------------------------------------------------------------
ErrCode DeviceManager::GetVideoInputDevices(std::vector<MediaDevice>& devices)
{
	std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> video_device_info(
		webrtc::VideoCaptureFactory::CreateDeviceInfo());

	if (!video_device_info) {
		LOG_ERR("Failed to create video device info");
		return ERR_FAILED;
	}

	int num_devices = video_device_info->NumberOfDevices();
	LOG_INF("Found {} video input devices", num_devices);

	for (int i = 0; i < num_devices; i++) {
		char device_name[256] = { 0 };
		char device_id[256] = { 0 };

		if (video_device_info->GetDeviceName(
			i, device_name, sizeof(device_name), device_id, sizeof(device_id)) == 0) {
			MediaDevice device;
			device.index = i;
			device.name = device_name;
			device.id = device_id;
			device.type = MediaDeviceType::VideoInput;
			devices.push_back(device);
			LOG_INF("Video device {}: name='{}', id='{}'", i, device_name, device_id);
		}
	}

	return ERR_OK;
}

} // namespace p2pms